#' Watches code and tests for changes, rerunning tests as appropriate.
#'
#' @description
#' `r lifecycle::badge("superseded")`
#'
#' The idea behind `auto_test()` is that you just leave it running while
#' you develop your code.  Every time you save a file it will be automatically
#' tested and you can easily see if your changes have caused any test
#' failures.
#'
#' The current strategy for rerunning tests is as follows:
#'
#' - if any code has changed, then those files are reloaded and all tests
#'   rerun
#' - otherwise, each new or modified test is run
#' @seealso [auto_test_package()]
#' @export
#' @param code_path path to directory containing code
#' @param test_path path to directory containing tests
#' @param reporter test reporter to use
#' @param env environment in which to execute test suite.
#' @param hash Passed on to [watch()]. When FALSE, uses less accurate
#'   modification time stamps, but those are faster for large files.
#' @keywords internal
auto_test <- function(
  code_path,
  test_path,
  reporter = default_reporter(),
  env = test_env(),
  hash = TRUE
) {
  reporter <- find_reporter(reporter)
  code_path <- normalizePath(code_path)
  test_path <- normalizePath(test_path)

  # Start by loading all code and running all tests
  source_dir(code_path, env = env)
  test_dir(test_path, env = env, reporter = reporter$clone(deep = TRUE))

  # Next set up watcher to monitor changes
  watcher <- function(added, deleted, modified) {
    changed <- normalizePath(c(added, modified))

    tests <- changed[starts_with(changed, test_path)]
    code <- changed[starts_with(changed, code_path)]

    if (length(code) > 0) {
      # Reload code and rerun all tests
      cat("Changed code: ", paste0(basename(code), collapse = ", "), "\n")
      cat("Rerunning all tests\n")
      source_dir(code_path, env = env)
      test_dir(test_path, env = env, reporter = reporter$clone(deep = TRUE))
    } else if (length(tests) > 0) {
      # If test changes, rerun just that test
      cat("Rerunning tests: ", paste0(basename(tests), collapse = ", "), "\n")
      test_files(tests, env = env, reporter = reporter$clone(deep = TRUE))
    }

    TRUE
  }
  watch(c(code_path, test_path), watcher, hash = hash)
}

#' @param pkg path to package
#' @export
#' @param reporter test reporter to use
#' @param hash Passed on to [watch()].  When FALSE, uses less accurate
#'   modification time stamps, but those are faster for large files.
#' @rdname auto_test
auto_test_package <- function(
  pkg = ".",
  reporter = default_reporter(),
  hash = TRUE
) {
  reporter <- find_reporter(reporter)

  path <- pkgload::pkg_path(pkg)
  package <- pkgload::pkg_name(path)

  code_path <- file.path(path, c("R", "src"))
  code_path <- code_path[file.exists(code_path)]
  code_path <- normalizePath(code_path)
  test_path <- normalizePath(file.path(path, "tests", "testthat"))

  # Start by loading all code and running all tests
  local_assume_not_on_cran()
  pkgload::load_all(path)
  test_dir(
    test_path,
    package = package,
    reporter = reporter$clone(deep = TRUE),
    stop_on_failure = FALSE
  )

  # Next set up watcher to monitor changes
  watcher <- function(added, deleted, modified) {
    changed <- normalizePath(c(added, modified))

    tests <- changed[starts_with(changed, test_path)]
    code <- changed[starts_with(changed, code_path)]

    # Remove helper from test and add it to code (if a helper changed,
    # like for code, reload all and rerun all tests)
    helper <- tests[starts_with(basename(tests), "helper-")]
    tests <- setdiff(tests, helper)
    code <- c(code, helper)

    if (length(code) > 0) {
      # Reload code and rerun all tests
      cat("Changed code: ", paste0(basename(code), collapse = ", "), "\n")
      cat("Rerunning all tests\n")
      pkgload::load_all(path, quiet = TRUE)
      test_dir(
        test_path,
        package = package,
        reporter = reporter$clone(deep = TRUE)
      )
    } else if (length(tests) > 0) {
      # If test changes, rerun just that test
      cat("Rerunning tests: ", paste0(basename(tests), collapse = ", "), "\n")
      env <- env_clone(asNamespace(package))
      test_files(
        test_dir = test_path,
        test_package = package,
        test_paths = tests,
        env = env,
        reporter = reporter$clone(deep = TRUE)
      )
    }

    TRUE
  }
  watch(c(code_path, test_path), watcher, hash = hash)
}

# Helpers -----------------------------------------------------------------

starts_with <- function(string, prefix) {
  substr(string, 1, nchar(prefix)) == prefix
}
